# -*- coding: utf-8 -*-
# Copyright 2016-2017 Versada <https://versada.eu/>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging
import sys
import unittest

from openerp import exceptions

import raven

from .. import initialize_raven
from ..logutils import OdooSentryHandler


def log_handler_by_class(logger, handler_cls):
    for handler in logger.handlers:
        if isinstance(handler, handler_cls):
            yield handler


def remove_logging_handler(logger_name, handler_cls):
    '''Removes handlers of specified classes from a :class:`logging.Logger`
    with a given name.

    :param string logger_name: name of the logger

    :param handler_cls: class of the handler to remove. You can pass a tuple of
        classes to catch several classes
    '''
    logger = logging.getLogger(logger_name)
    for handler in log_handler_by_class(logger, handler_cls):
        logger.removeHandler(handler)


class InMemoryClient(raven.Client):
    '''A :class:`raven.Client` subclass which simply stores events in a list.

    Extended based on the one found in raven-python to avoid additional testing
    dependencies: https://git.io/vyGO3
    '''

    def __init__(self, **kwargs):
        self.events = []
        super(InMemoryClient, self).__init__(**kwargs)

    def is_enabled(self):
        return True

    def send(self, **kwargs):
        self.events.append(kwargs)

    def has_event(self, event_level, event_msg):
        for event in self.events:
            if (event.get('level') == event_level and
                    event.get('message') == event_msg):
                return True
        return False


class TestClientSetup(unittest.TestCase):

    def setUp(self):
        super(TestClientSetup, self).setUp()
        self.logger = logging.getLogger(__name__)

        # Sentry is enabled by default, so the default handler will be added
        # when the module is loaded. After that, subsequent calls to
        # setup_logging will not re-add our handler. We explicitly remove
        # OdooSentryHandler handler so we can test with our in-memory client.
        remove_logging_handler('', OdooSentryHandler)

    def assertEventCaptured(self, client, event_level, event_msg):
        self.assertTrue(
            client.has_event(event_level, event_msg),
            msg=u'Event: "%s" was not captured' % event_msg
        )

    def assertEventNotCaptured(self, client, event_level, event_msg):
        self.assertFalse(
            client.has_event(event_level, event_msg),
            msg=u'Event: "%s" was captured' % event_msg
        )

    def test_initialize_raven_sets_dsn(self):
        config = {
            'sentry_enabled': True,
            'sentry_dsn': 'http://public:secret@example.com/1',
        }
        client = initialize_raven(config, client_cls=InMemoryClient)
        self.assertEqual(client.remote.base_url, 'http://example.com')

    def test_capture_event(self):
        config = {
            'sentry_enabled': True,
            'sentry_dsn': 'http://public:secret@example.com/1',
        }
        level, msg = logging.WARNING, 'Test event, can be ignored'
        client = initialize_raven(config, client_cls=InMemoryClient)
        self.logger.log(level, msg)
        self.assertEventCaptured(client, level, msg)

    def test_ignore_exceptions(self):
        config = {
            'sentry_enabled': True,
            'sentry_dsn': 'http://public:secret@example.com/1',
            'sentry_ignore_exceptions': 'odoo.exceptions.UserError',
        }
        level, msg = logging.WARNING, 'Test UserError'
        client = initialize_raven(config, client_cls=InMemoryClient)

        handlers = list(
            log_handler_by_class(logging.getLogger(), OdooSentryHandler)
        )
        self.assertTrue(handlers)
        handler = handlers[0]
        try:
            raise exceptions.UserError(msg)
        except exceptions.UserError:
            exc_info = sys.exc_info()
        record = logging.LogRecord(
            __name__, level, __file__, 42, msg, (), exc_info)
        handler.emit(record)
        self.assertEventNotCaptured(client, level, msg)
